#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include "dept150_cholesky_mgpu.h"
#include "mkl_lapack.h"


int main(int argc, char** argv)
{
	double* A = NULL;
	int i, j;
	int N = 6;
	int block_size = 3;
	if(argc >=  2)
	{
		N = atoi(argv[1]);
		block_size = atoi(argv[2]);
	}	
	A = getA16(N);

	int n = N;
	int info = -10;

	gpu_based_matrix_t gpu_matrix;

	checkCudaErrors(cudaGetDeviceCount(&(gpu_matrix.gpu_n)));
	gpu_matrix.order = N;
	gpu_matrix.block_size = block_size;

	cublasHandle_t* handle = (cublasHandle_t *)malloc(gpu_matrix.gpu_n * sizeof(cublasHandle_t));
	
	if(initCublasHandles(handle, gpu_matrix.gpu_n))
	{
		printf("CUBLAS HANDLE INITIALIZATION FAILED\n");
		return EXIT_FAILURE;
	}
	
	if(copyMatrixToDevices(A, &gpu_matrix,handle ))
	{
		printf("INITIALIZATION OF DEVICE MATRIX FAILED");
		return EXIT_FAILURE;
	}

	clock_t start_time, end_time;
	start_time = clock();
	dept150_error_t ret_status = factorize_gpu_based_matrix(gpu_matrix, handle);	
	end_time = clock();

	double *result_matrix = (double*)malloc(N*N*sizeof(double));
	copyMatrixFromDevices(result_matrix, gpu_matrix);

	printf("Start MKL fact and check if matrix equlas to MKL result\n");
	MKL_INT mkl_info;
	dpotrf("L", &N, A,  &N, &mkl_info);

	if(mkl_info)
	{
		printf("MKL FACTORIZATION FAILED mkl_info - %d\n", mkl_info);
		return EXIT_FAILURE;
	}

	bool hasErrors = false;
	for(int i = 0; i < N; i++)
	{
		for(int j =0; j < N; j++)
		{
			if (fabs( result_matrix[IDF2(i, j, N)] - A[IDF2(i, j,N)] ) > 1e-6)
			{
				hasErrors = true;
				printf("For element [%i, %i] value of my %f value of mkl %f\n", i, j, result_matrix[IDF2(i, j, N)], A[IDF2(i, j,N)]); 
			}
		}
	}

	if(!hasErrors)
	{
		printf("Comparing with MKL success\n");
	}
	
	//printf("Matrix after factorize \n");
//	print_matrix(N, N, result_matrix, N);


	printf("\nTime : %.5f\n",(float)(end_time - start_time)/CLOCKS_PER_SEC);
	
	free(A);
	free(result_matrix);

	cublasStatus_t stat;
	for(int i = 0; i < gpu_matrix.gpu_n; i++)
	{
		checkCudaErrors(cudaSetDevice(i));
		checkCudaErrors(cudaFree(gpu_matrix.gpu_matrix_blocks[i]));
	}

	free(gpu_matrix.gpu_matrix_blocks);
}



